{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 用 Flask-Script 扩展 Flask\r\n",
    "\r\n",
    "`Flask Extensions` 是指那些你可以安装来扩展Flask功能的软件包。`Flask Extensions`背后的想法是通过提供一种一致且可预测的方式,在Flask下整合包。要查看所有可用的扩展请访问 http://flask.pocoo.org/extensions/。该页面包含从发送邮件到构建成熟的管理界面的各种包。请记住，您并不局限于使用 `Flask Extensions` 来扩展 Flask，事实上，您可以使用来自 Python 标准库或 PyPI 的任何包。本课余下的部分将讨论如何安装和集成一个名为 Flask-Script 的有用的 `Flask Extensions`。\r\n",
    "\r\n",
    "##  Flask-Script Extension\r\n",
    "\r\n",
    "`Flask-Script` 是一个不错的小扩展，它允许我们创建命令行接口，运行服务器，在应用程序上下文中启动 Python shell，使某些变量在 shell 中自动可用等等。\r\n",
    "\r\n",
    "回想一下，在 前面的Flask基础课程中，我们讨论过，为了在特定的主机和端口上运行开发服务器，我们需要将它们作为关键字参数传递给 run ()方法，如下所示:\r\n",
    "\r\n",
    "```\r\n",
    "if __name__ == \"__main__\":\r\n",
    "    app.run(debug=True, host=\"127.0.0.10\", port=9000)\r\n",
    "```\r\n",
    "\r\n",
    "问题是这种方法不是很灵活，更好的方法是在启动服务器时将 host 和 port 作为命令行选项传递给服务器。Flask-Script 扩展允许我们这样做。事不宜迟，让我们通过输入以下命令来\r\n",
    "\r\n",
    "### 安装 Flask-Script:\r\n",
    "\r\n",
    "`pip install flask-script`\r\n",
    "\r\n",
    "### 使用 Flask-Script \r\n",
    "首先从 `flask_script`  包导入 `Manager`  类，并通过向其传递应用程序实例来实例化 `Manager` 对象。这就是集成 Flask 扩展的方法，从 扩展包中导入必要的类，并通过向其传递应用程序实例来实例化它。打开 main4.py 并修改文件如下:\r\n",
    "\r\n",
    "注: `Manager`  类 在当前的Flask 2.0上还不能兼容, 只能用于Flask 2.0以下.\r\n",
    "\r\n",
    "```\r\n",
    "from flask import Flask, render_template\r\n",
    "from flask_script import Manager\r\n",
    "\r\n",
    "app = Flask(__name__)\r\n",
    "manager = Manager(app)\r\n",
    "```\r\n",
    "\r\n",
    "我们刚才创建的 Manager 对象还提供了 run ()方法，但是除了启动开发服务器之外，它还可以解析命令行参数。用 manager.run ()替换 app.run (debug = True)行。此时 main4.py 应该是这样的:\r\n",
    "\r\n",
    "```\r\n",
    "from flask import Flask, render_template\r\n",
    "from flask_script import Manager\r\n",
    "\r\n",
    "app = Flask(__name__)\r\n",
    "manager = Manager(app)\r\n",
    "\r\n",
    "@app.route('/')\r\n",
    "def index():\r\n",
    "    return render_template('index.html', name='Jerry')\r\n",
    "\r\n",
    "@app.route('/user/<int:user_id>/')\r\n",
    "def user_profile(user_id):\r\n",
    "    return \"Profile page of user #{}\".format(user_id)\r\n",
    "\r\n",
    "@app.route('/books/<genre>/')\r\n",
    "def books(genre):\r\n",
    "    return \"All Books in {} category\".format(genre)\r\n",
    "\r\n",
    "if __name__ == \"__main__\":    \r\n",
    "    manager.run()\r\n",
    "```    \r\n",
    "\r\n",
    "我们的应用程序现在可以访问一些基本的命令:\r\n",
    "\r\n",
    "`python main4.py`\r\n",
    "\r\n",
    "如输出所示，目前我们有两个命令: shell 和 runserver。\r\n",
    "\r\n",
    "Runserver 命令启动 web 服务器。默认情况下，它在端口5000上以127.0.0.1启动开发服务器。查看任何命令的可用选项。键入 -- help 后跟命令名。例如:\r\n",
    "\r\n",
    "`python main4.py runserver --help`\r\n",
    "\r\n",
    "Runserver 命令最常用的选项是 -- host 和 -- post，它允许我们在特定的接口和端口启动开发服务器。例如:\r\n",
    "\r\n",
    "`python main4.py runserver --host=127.0.0.2 --port 8000`\r\n",
    "\r\n",
    "缺省情况下，runserver 命令在没有调试器和重新加载程序的情况下启动服务器。我们可以打开调试器并手动重新加载，如下所示:\r\n",
    "\r\n",
    "`python main4.py runserver -d -r`\r\n",
    "\r\n",
    "启动调试器和重新加载程序的一种简单得多的方法是将应用程序实例(app)的调试属性设置为 True。打开 main4.py 并修改文件如下:\r\n",
    "\r\n",
    "```\r\n",
    "#...\r\n",
    "app = Flask(__name__)\r\n",
    "app.debug = True\r\n",
    "manager = Manager(app)\r\n",
    "#...\r\n",
    "```\r\n",
    "\r\n",
    "现在让我们探索 shell 命令。\r\n",
    "\r\n",
    "Shell 命令在 Flask 应用程序上下文中启动 Python shell。这不仅意味着应用程序和请求上下文提供的所有对象在控制台内都可用，而且无需创建应用程序 或 请求上下文。要启动 shell，输入以下命令。\r\n",
    "\r\n",
    "`python main4.py shell`\r\n",
    "\r\n",
    "现在让我们尝试访问一些对象。\r\n",
    "\r\n",
    "```\r\n",
    "PS D:\\python_blard\\FLASK\\flask_project\\笔记> python main4.py shell\r\n",
    "\r\n",
    "In [1]: from flask import current_app, url_for, request\r\n",
    "\r\n",
    "In [2]: current_app.name\r\n",
    "Out[2]: 'main4'\r\n",
    "\r\n",
    "In [3]: url_for(\"user_profile\", user_id=10)\r\n",
    "Out[3]: '/user/10/'\r\n",
    "\r\n",
    "In [4]: request.path\r\n",
    "Out[4]: '/'\r\n",
    "```\r\n",
    "\r\n",
    "正如预期的那样，我们可以访问所需的对象，而无需创建应用程序或请求上下文。\r\n",
    "\r\n",
    "## 创建命令\r\n",
    "\r\n",
    "一旦创建了 Manager 实例，我们就可以创建我们自己的命令了:\r\n",
    "\r\n",
    "1. 使用 Command 类. \r\n",
    "2. 使用 @command 装饰器.\r\n",
    "\r\n",
    "复制 main4.py 为 main5.py 并打开添加 Faker 类如下:\r\n",
    "\r\n",
    "#...\r\n",
    "class Faker(Command):\r\n",
    "    'A command to add fake data to the tables'\r\n",
    "    def run(self):\r\n",
    "        # add logic here\r\n",
    "        print(\"Fake data entered\")\r\n",
    "\r\n",
    "manager.add_command(\"faker\", Faker())\r\n",
    "#...\r\n",
    "\r\n",
    "现在回到 terminal，再次运行 `python main5.py`\r\n",
    "\r\n",
    "注意，除了 shell 和 runserver 之外，我们现在还有一个 `faker` `命令的条目。faker` 命令前面的描述来自伪装者类中的 doc 字符串。要运行它，输入以下命令: `python main2.py faker`\r\n",
    "\r\n",
    "## 使用 装饰器 创建命令\r\n",
    "\r\n",
    "使用 Command 类创建命令有点冗长。或者，我们可以使用 Manager 实例的 @command 装饰器 。打开 main5.py 并修改文件如下:\r\n",
    "\r\n",
    "#...\r\n",
    "manager.add_command(\"faker\", Faker())\r\n",
    "\r\n",
    "@manager.command\r\n",
    "def foo():\r\n",
    "    \"Just a simple command\"\r\n",
    "    print(\"foo command executed\")\r\n",
    "\r\n",
    "@app.route('/')\r\n",
    "#...\r\n",
    "\r\n",
    "我们创建了一个简单的命令 foo，它在调用时打印执行的 foo 命令。@command 装饰器 会自动将该命令添加到现有的 Manager 实例中，因此我们不需要调用 add_command() 方法。\r\n",
    "\r\n",
    "## 自动导入对象\r\n",
    "\r\n",
    "在 shell 中导入大量对象可能会很乏味。使用 `Flask-Script` ，我们可以使对象在 shell 中可用，而不需要显式地导入它们。\r\n",
    "\r\n",
    "Shell 命令启动一个终端Shell。Shell的构造函数接受一个名为 `make_context` 的关键字参数。传递给 `make_context` 的参数 必须是一个返回值为字典的可调用体(比如一个返回值为字典的函数)。默认情况下，可调用体 返回一个只包含应用程序实例即应用程序的字典。这意味着，默认情况下，在 shell 中，您只能访问应用程序实例(app)，无需显式导入它。要覆盖这个默认值，请分配一个新的可调用体给 `make_context` ，该可调用体返回要在 shell 中访问的对象的字典。\r\n",
    "\r\n",
    "\r\n",
    "打开 main5.py 并在 foo ()函数之后添加以下代码。\r\n",
    "\r\n",
    "#...\r\n",
    "from flask_script import Manager, Command, Shell\r\n",
    "\r\n",
    "#...\r\n",
    "def shell_context():\r\n",
    "    import os, sys\r\n",
    "    return dict(app=app, os=os, sys=sys)\r\n",
    "\r\n",
    "manager.add_command(\"shell\", Shell(make_context=shell_context))\r\n",
    "#...\r\n",
    "\r\n",
    "在这里，我们将一个可调用的名为 shell_context 的上下文分配给 make_context 关键字参数。函数的作用是: 返回一个包含三个对象的字典: app，os 和 sys. 因此，在 shell 中，我们可以访问这些对象，而无需显式地导入它们。\r\n",
    "\r\n",
    "运行 `python .\\main5.py shell`\r\n",
    "\r\n",
    "```\r\n",
    "PS D:\\python_blard\\FLASK\\flask_project\\笔记> python .\\main5.py shell\r\n",
    "\r\n",
    "In [1]: app\r\n",
    "Out[1]: <Flask 'main5'>\r\n",
    "\r\n",
    "In [2]: os.name\r\n",
    "Out[2]: 'nt'\r\n",
    "\r\n",
    "In [3]: sys.platform\r\n",
    "Out[3]: 'win32'\r\n",
    "```\r\n"
   ],
   "metadata": {}
  }
 ],
 "metadata": {
  "orig_nbformat": 4,
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}